﻿namespace EasyWeb
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Xml.Linq;
    using EasyWeb.Configuration;
    using EasyWeb.Data.EntityFramework.Models;

    internal static class EWModelHelpers
    {
        #region Culture

        public static EWCultureInfo ToDomain(this EWCultureModel entity)
        {
            if (entity == null) return null;

            return new EWCultureInfo(entity.Id, entity.Name, entity.Enabled);
        }

        #endregion

        #region Entity

        public static EWEntityDefinition ToDomain(this EWEntityDefinitionModel entity)
        {
            if (entity == null) return null;

            var domain = new EWEntityDefinition();
            domain.StateBag.DisableChangeTracking();

            domain.Id = entity.Id;
            domain.InternalName = entity.InternalName;
            domain.DisplayName = entity.DisplayName.ToText();
            domain.Description = entity.Description.ToText();
            domain.Created = entity.Created.ToUtc();
            domain.CreatedBy = entity.CreatedBy;
            domain.Modified = entity.Modified.ToUtc();
            domain.ModifiedBy = entity.ModifiedBy;
            domain.Timestamp = entity.Timestamp;

            domain.StateBag.EnableChangeTracking();

            return domain;
        }

        public static EWEntityDefinitionModel ToEntity(this EWEntityDefinition domain)
        {
            if (domain == null) return null;

            return new EWEntityDefinitionModel()
            {
                Id = domain.Id,
                InternalName = domain.InternalName,
                DisplayName = domain.DisplayName.ToText(),
                Description = domain.Description.ToText(),
                Created = domain.Created.ToUtc(),
                CreatedBy = domain.CreatedBy,
                Modified = domain.Modified.ToUtc(),
                ModifiedBy = domain.ModifiedBy,
                Timestamp = domain.Timestamp,
            };
        }

        #endregion

        #region Field

        public static EWFieldDefinition ToDomain(this EWFieldDefinitionModel entity)
        {
            if (entity == null) return null;

            var fields = EWHelpers.Async.RunSync(async () => await EWConfigurationManager.GetFieldsAsync());

            var type = fields.FirstOrDefault(c => c.Id == entity.TypeId).Type;
            var domain = (EWFieldDefinition)EWHelpers.TypeHelpers.Create(type);
            domain.StateBag.DisableChangeTracking();

            domain.Id = entity.Id;
            domain.EntityDefinitionId = entity.EntityDefinitionId;
            domain.Created = entity.Created.ToUtc();
            domain.CreatedBy = entity.CreatedBy;
            domain.Modified = entity.Modified.ToUtc();
            domain.ModifiedBy = entity.ModifiedBy;
            domain.Timestamp = entity.Timestamp;

            var info = new EWSerializationInfo();
            var xml = XDocument.Parse(entity.Value, LoadOptions.None);
            foreach (var element in xml.Root.Elements("value"))
            {
                var key = element.Attribute("name").Value;
                var value = element.Value;

                info.AddValue(key, value);
            }

            domain.Deserialize(info);
            domain.StateBag.EnableChangeTracking();

            return domain;
        }

        public static EWFieldDefinitionModel ToEntity(this EWFieldDefinition domain)
        {
            if (domain == null) return null;

            var fields = EWHelpers.Async.RunSync(async () => await EWConfigurationManager.GetFieldsAsync());

            var model = new EWFieldDefinitionModel()
            {
                Id = domain.Id,
                EntityDefinitionId = domain.EntityDefinitionId,
                Created = domain.Created.ToUtc(),
                CreatedBy = domain.CreatedBy,
                Modified = domain.Modified.ToUtc(),
                ModifiedBy = domain.ModifiedBy,
                Timestamp = domain.Timestamp,
                TypeId = fields.FirstOrDefault(c => c.Type == domain.GetType()).Id,
            };

            var info = new EWSerializationInfo();
            domain.Serialize(info);
            var xml = new XDocument(new XElement("values"));
            foreach (var item in info)
            {
                var key = item.Key;
                var value = item.Value;

                if (value != null)
                {
                    var element = new XElement("value",
                        new XAttribute("name", key),
                        new XText(value));
                    xml.Root.Add(element);
                }
            }

            model.Value = xml.ToString(SaveOptions.DisableFormatting);

            return model;
        }

        #endregion

        #region Selection

        public static EWSelectionDefinition ToDomain(this EWSelectionDefinitionModel entity)
        {
            if (entity == null) return null;

            var domain = new EWSelectionDefinition();
            domain.StateBag.DisableChangeTracking();

            domain.Id = entity.Id;
            domain.InternalName = entity.InternalName;
            domain.DisplayName = entity.DisplayName.ToText();
            domain.Created = entity.Created.ToUtc();
            domain.CreatedBy = entity.CreatedBy;
            domain.Modified = entity.Modified.ToUtc();
            domain.ModifiedBy = entity.ModifiedBy;
            domain.Timestamp = entity.Timestamp;

            domain.StateBag.EnableChangeTracking();

            return domain;
        }

        public static EWSelectionDefinitionModel ToEntity(this EWSelectionDefinition domain)
        {
            if (domain == null) return null;

            return new EWSelectionDefinitionModel()
            {
                Id = domain.Id,
                InternalName = domain.InternalName,
                DisplayName = domain.DisplayName.ToText(),
                Created = domain.Created.ToUtc(),
                CreatedBy = domain.CreatedBy,
                Modified = domain.Modified.ToUtc(),
                ModifiedBy = domain.ModifiedBy,
                Timestamp = domain.Timestamp,
            };
        }

        #endregion

        #region Selection item

        public static EWSelectionItemDefinition ToDomain(this EWSelectionItemDefinitionModel entity)
        {
            if (entity == null) return null;

            var domain = new EWSelectionItemDefinition();
            domain.StateBag.DisableChangeTracking();

            domain.Id = entity.Id;
            domain.Key = entity.Key;
            domain.Value = entity.Value.ToText();
            domain.SelectionDefinitionId = entity.SelectionDefinitionId;
            domain.Created = entity.Created.ToUtc();
            domain.CreatedBy = entity.CreatedBy;
            domain.Modified = entity.Modified.ToUtc();
            domain.ModifiedBy = entity.ModifiedBy;
            domain.Timestamp = entity.Timestamp;

            domain.StateBag.EnableChangeTracking();

            return domain;
        }

        public static EWSelectionItemDefinitionModel ToEntity(this EWSelectionItemDefinition domain)
        {
            if (domain == null) return null;

            return new EWSelectionItemDefinitionModel()
            {
                Id = domain.Id,
                Key = domain.Key,
                Value = domain.Value.ToText(),
                SelectionDefinitionId = domain.SelectionDefinitionId,
                Created = domain.Created.ToUtc(),
                CreatedBy = domain.CreatedBy,
                Modified = domain.Modified.ToUtc(),
                ModifiedBy = domain.ModifiedBy,
                Timestamp = domain.Timestamp,
            };
        }

        #endregion

        #region DateTime

        public static DateTime ToUtc(this DateTime source)
        {
            if (source.Kind == DateTimeKind.Utc)
            {
                return source;
            }

            return new DateTime(source.Year, source.Month, source.Day, source.Hour, source.Minute, source.Second, source.Millisecond, DateTimeKind.Utc);
        }

        public static DateTime? ToUtc(this DateTime? source)
        {
            if (!source.HasValue)
            {
                return null;
            }

            return source.Value.ToUtc();
        }

        #endregion

        #region Configuration

        public static class Configuration
        {
            #region Default language

            public static class DefaultLanguage
            {
                public static EWDefaultLanguageConfiguration ToDomain(EWConfigurationModel entity)
                {
                    if (entity == null) return null;

                    var result = new EWDefaultLanguageConfiguration()
                    {
                        Id = entity.Id,
                        Timestamp = entity.Timestamp,
                    };

                    if (!string.IsNullOrWhiteSpace(entity.Value1))
                    {
                        var xml = ParseXml(entity.Value1);

                        result.DefaultLanguage = xml.Item1;
                        result.UseBrowserSetting = xml.Item2;
                        result.UseDefault = xml.Item3;
                    }

                    return result;
                }

                public static EWConfigurationModel ToEntity(EWDefaultLanguageConfiguration domain)
                {
                    if (domain == null) return null;

                    var xml = CreateConfigurationXmlDocument();
                    xml.Root.Add(new XElement("value", domain.DefaultLanguage));
                    xml.Root.Add(new XElement("client", domain.UseBrowserSetting));
                    xml.Root.Add(new XElement("default", domain.UseDefault));

                    return new EWConfigurationModel()
                    {
                        Id = domain.Id,
                        Name = "default_language",
                        Timestamp = domain.Timestamp,
                        Value1 = xml.ToString(SaveOptions.DisableFormatting)
                    };
                }

                public static Tuple<string, bool, bool> ParseXml(string xml)
                {
                    var doc = XDocument.Parse(xml, LoadOptions.None);

                    return new Tuple<string, bool, bool>(
                        doc.Root.Element("value").Value,
                        bool.Parse(doc.Root.Element("client").Value),
                        bool.Parse(doc.Root.Element("default").Value));
                }
            }

            #endregion

            #region System prefix

            public static class SystemPrefix
            {
                public static EWSystemPrefixConfiguration ToDomain(EWConfigurationModel entity)
                {
                    if (entity == null) return null;

                    var result = new EWSystemPrefixConfiguration()
                    {
                        Id = entity.Id,
                        Timestamp = entity.Timestamp,
                    };

                    if (!string.IsNullOrWhiteSpace(entity.Value1))
                    {
                        var xml = ParseXml(entity.Value1);

                        result.Value = xml.Item1;
                    }

                    return result;
                }

                public static EWConfigurationModel ToEntity(EWSystemPrefixConfiguration domain)
                {
                    if (domain == null) return null;

                    var xml = CreateConfigurationXmlDocument();
                    xml.Root.Value = domain.Value;

                    return new EWConfigurationModel()
                    {
                        Id = domain.Id,
                        Name = "system_prefix",
                        Timestamp = domain.Timestamp,
                        Value1 = xml.ToString(SaveOptions.DisableFormatting)
                    };
                }

                public static Tuple<string> ParseXml(string xml)
                {
                    var doc = XDocument.Parse(xml, LoadOptions.None);

                    return new Tuple<string>(doc.Root.Value);
                }
            }

            #endregion

            private static XDocument CreateConfigurationXmlDocument()
            {
                return new XDocument(new XElement("value"));
            }
        }

        #endregion
    }
}
